type InputDateType = string | number | null | undefined | Date
/**
 * 是否是一个有效的日期
 * @param s 字符串，数字，日期对象
 * @returns Boolean
 */
export const isDate = function (date: InputDateType) {
  if (date === null || date === undefined) return false
  if (typeof date === 'string') {
    // 兼容ios,mac
    date = date.replace(/-/g, '/')
  }
  if (Number.isNaN(new Date(date).getTime())) return false
  if (Array.isArray(date)) return false // deal with `new Date([ new Date() ]) -> new Date()`
  return true
}

export const toDate = function (date: InputDateType) {
  return isDate(date) ? new Date(date as any) : null
}

/**
 * 是否是一个有效的iso格式日期
 * @param s 字符串 console.log(isIsoDate('2011-10-05T14:48:00.000Z')) console.log(isIsoDate('2018-11-10T11:22:33+00:00'));
 * @returns Boolean
 */
export function isIsoDate(str: string) {
  if (!/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/.test(str)) return false
  const d = new Date(str)
  return d.toISOString() === str
}

// eslint-disable-next-line regexp/no-misleading-capturing-group
export const REGEX_PARSE = /^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[T\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/i
export function parseDate(date: InputDateType, utc?: boolean) {
  /* 解析输入的时间为标准date对象 */
  if (!date) return new Date() // 未传时间默认为当前
  if (date instanceof Date) return new Date(date)
  // 支持10位数秒时间戳统一转换为13位毫秒时间戳
  if (typeof date === 'number') {
    if (`${date}`.length === 10) date = date * 1000
    return new Date(date)
  }
  if (typeof date === 'string' && !/Z$/i.test(date)) {
    const d: any[] = date.match(REGEX_PARSE)!
    if (d) {
      const m = (d[2] as unknown as number) - 1 || 0
      const ms = (d[7] || '0').substring(0, 3)
      if (utc) {
        return new Date(Date.UTC(d[1], m, d[3] || 1, d[4] || 0, d[5] || 0, d[6] || 0, ms))
      }
      return new Date(d[1], m, d[3] || 1, d[4] || 0, d[5] || 0, d[6] || 0, ms)
    }
  }

  return new Date(date) // everything else
}
/**
 * 返回传入时间的当月有多少天
 * @param {*} time 时间格式
 * @returns
 */
export function daysInMonth(time: InputDateType) {
  const day = parseDate(time)
  const year = day.getFullYear()
  const month = day.getMonth() // 获取当前月份
  return new Date(year, month + 1, 0).getDate()
}

interface DatePlugin {
  monthEn: string
  monthZh: string
  weekZh: string
  weekEn: string
  a: string
  A: string
}
// const monthEnAll = "January,February,March,April,May,June,July,August,September,October,November,December"
/**
 * 格式化时间戳 Y-m-d H:i:s
 * @param {string | number | Date} timestamp 时间戳或者日期对象   秒或者毫秒(10位数或者13位数)
 * @param {string} format Y-m-d H:i:s
 * @param {string} plugin 可以通过传递月份，星期，上午，下午字符串自定义显示{monthEn,monthZh,weekZh,weekEn,a,A}{ 比如 { monthZh: '第一月,第二月....,第十二月' }中间,作为分隔符
 * @note 标识  示例  描述
 * @note YY  18  年，两位数
 * @note YYYY  2018  年，四位数
 * @note M  1-12  月，从1开始
 * @note MM  01-12  月，两位数字
 * @note MMM  Jan-Dec 月，英文缩写
 * @note MMMM 一月-十二月 月，中文
 * @note W    Sun-Sat 英文星期缩写
 * @note WW   星期天-星期六 中文星期
 * @note D  1-31  日
 * @note DD  01-31  日，两位数
 * @note H  0-23  24小时
 * @note HH  00-23  24小时，两位数
 * @note h  1-12  12小时
 * @note hh  01-12  12小时，两位数
 * @note m  0-59  分钟
 * @note mm  00-59  分钟，两位数
 * @note s  0-59  秒
 * @note ss  00-59  秒，两位数
 * @note S  0-9  毫秒（百），一位数
 * @note SS  00-99  毫秒（十），两位数
 * @note SSS  000-999  毫秒，三位数
 * @note A  AM / PM  上/下午，大写
 * @note a  am / pm  上/下午，小写
 * @return {string} 返回指定格式的时间字符串
 */
export function formatTime(timeStamp: InputDateType, format = 'YYYY-MM-DD hh:mm:ss', plugin = {} as DatePlugin) {
  plugin = Object.assign(
    {},
    {
      monthEn: 'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Spt,Oct,Nov,Dec',
      monthZh: '一月,二月,三月,四月,五月,六月,七月,八月,九月,十月,十一月,十二月',
      weekZh: '星期天,星期一,星期二,星期三,星期四,星期五,星期六',
      weekEn: 'Sun,MON,Tues,Wed,Thur,Fri,Sat',
      a: 'am,pm',
      A: 'AM,PM',
    },
    plugin || {},
  ) as DatePlugin

  const _date = parseDate(timeStamp)
  const Y = _date.getFullYear()
  const orimonth = _date.getMonth()
  const m = orimonth + 1
  const d = _date.getDate()
  const w = _date.getDay()
  const H = _date.getHours()
  const i = _date.getMinutes()
  const s = _date.getSeconds()
  const mmm = _date.getMilliseconds()

  const REGEX_FORMAT = /\[([^\]]+)\]|Y{1,4}|M{1,4}|W{1,2}|D{1,2}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g

  const addZero = (num: number) => (num < 10 ? '0' : '') + num
  const hour = (h: number) => h % 12 || 12 // 24小时制转12小时制
  const addZeroSss = (h: number) => (h < 100 ? (h < 10 ? `00${h}` : `0${h}`) : `${h}`) // 3位数毫秒

  const monthEn = plugin.monthEn.split(',')
  const monthZh = plugin.monthZh.split(',')

  const weekEn = plugin.weekEn.split(',')
  const weekZh = plugin.weekZh.split(',')

  const meridiemFunc = (hour: number, isLowercase: boolean) => {
    const a = isLowercase ? plugin.a.split(',') : plugin.A.split(',')
    const m = hour < 12 ? a[0] : a[1]
    return m
  }

  const matches = {
    YY: String(Y).slice(-2),
    YYYY: Y,
    M: m,
    MM: addZero(m),
    MMM: monthEn[orimonth],
    MMMM: monthZh[orimonth],
    W: weekEn[w],
    WW: weekZh[w],
    D: d,
    DD: addZero(d),
    H: String(H),
    HH: addZero(H),
    h: hour(H),
    hh: addZero(hour(H)),
    a: meridiemFunc(H, true),
    A: meridiemFunc(H, false),
    m: String(i),
    mm: addZero(i),
    s: String(s),
    ss: addZero(s),
    S: mmm,
    SS: addZero(mmm),
    SSS: addZeroSss(mmm),
  }
  return format.replace(REGEX_FORMAT, (match, $1) => $1 || matches[match as unknown as keyof typeof matches] || '')
}

/**
 * @description 输入时间戳，或者合法时间返回时间戳
 * @param {Any}  time 时间格式
 * @param {boolean}  ten 返回10位数时间戳
 * @return {number} 时间戳 13位
 */
export function parseDateToStamp(time: InputDateType, ten?: boolean) {
  if (ten) return ~~(parseDate(time).getTime() / 1000)
  return parseDate(time).getTime()
}

/**
 * @description 输入时间戳，或者合法时间返回与当前时间的差值
 * @return {number} 时间戳 13位
 */
export function diffToNow(time: number) {
  if (typeof time === 'number') {
    // 支持10位数秒时间戳统一转换为13位毫秒时间戳
    if (`${time}`.length === 10) time = +time * 1000
    return +new Date() - +time
  }
  return +new Date() - parseDateToStamp(time)
}

/**
 * 显示给定时间多久前，类似聊天信息发送时间
 * @param {number} milliseconds 时间戳，毫秒
 * @returns {string} 多久前
 */
export function timeAgo(milliseconds: number) {
  const UNITS = {
    年: 31557600000,
    月: 2629800000,
    天: 86400000,
    小时: 3600000,
    分钟: 60000,
    秒: 1000,
  }

  if (milliseconds < 10000) return '刚刚'
  let humanize = ''
  for (const key in UNITS) {
    if (milliseconds >= UNITS[key as unknown as keyof typeof UNITS]) {
      humanize = `${Math.floor(milliseconds / UNITS[key as unknown as keyof typeof UNITS]) + key}前`
      break
    }
  }
  return humanize || '刚刚'
}

const repeatStr = (count: number, str: string) => Array.from({ length: Math.max(count + 1, 0) }).join(str || '')

/**
 * 倒计时格式化工具
 * @param {number} milliseconds 时间戳，毫秒
 * @param {string} [format] 格式化方式
 */
export function countTimeFormat(milliseconds: number, format = 'w周d天h时m分s秒') {
  const REGEX_FORMAT = /\[([^\]]+)\]|w{1,9}|d{1,9}|h{1,9}|m{1,9}|s{1,9}|i{1,9}/g
  const UNITS = {
    w: 604800000,
    d: 86400000,
    h: 3600000,
    m: 60000,
    s: 1000,
    i: 1,
  }
  milliseconds = milliseconds * 1
  if (Number.isNaN(milliseconds)) return ''

  let temp = 0
  let index = 0
  // @ts-expect-error fix
  return format.replace(REGEX_FORMAT, (match, _$1) => {
    let digit = 0
    if (match.length > 1) {
      // 多个相同拆分 dd 拆分为d,d
      digit = match.length // repeatTimes(match)
      match = match[0]
    }
    const t = UNITS[match as unknown as keyof typeof UNITS]
    if (!t) return 0
    index++
    const t0 = ~~(index === 1 ? milliseconds / t : temp / t)
    const t1 = index === 1 ? milliseconds % t : temp % t
    temp = t1
    return repeatStr(digit - t0.toString().length, '0') + t0
  })
}

/**
 * 音视频时长之类的场景时，需要把时长秒数格式为 HH:mm:ss 的格式。
 * @param {number} s 秒
 * @returns HH:mm:ss
 */
export const formatSeconds = (s: number) => [Number.parseInt(`${s / 60 / 60}`), Number.parseInt(`${(s / 60) % 60}`), Number.parseInt(`${s % 60}`)].join(':').replace(/\b(\d)\b/g, '0$1')
